#include <process.h>
#include <ctime>
#include <Windows.h>
#include <iostream>
#include <fstream>
using namespace std;

const int number_of_threads = 8;
const int rows_matrix_A = 3000;
const int colums_matrix_A = 3000;
const int rows_matrix_B = 3000;
const int colums_matrix_B = 3000;

class WindowsThreading
{
private:
	static int* matrix_A;
	static int* matrix_B;
	static int* matrix_C;
	static int* matrix_B_transpose;
public:	
	static void ThreadFunction(void*thread_number);
	static void AssignMemory();
	static void DeleteMemory();
	static int MatrixGeneration();
	static int MatrixStore();
	static int MatrixTranspose();
};
int* WindowsThreading::matrix_A=NULL;
int* WindowsThreading::matrix_B=NULL;
int* WindowsThreading::matrix_C=NULL;
int* WindowsThreading::matrix_B_transpose=NULL;

void WindowsThreading::AssignMemory()
{
	matrix_A = new int[rows_matrix_A*colums_matrix_A];
	matrix_B = new int[rows_matrix_B*colums_matrix_B];
	matrix_C = new int[rows_matrix_B*colums_matrix_A];
	matrix_B_transpose = new int[colums_matrix_B*rows_matrix_B];
	cout<<"Memory Assigned\n";
}

int WindowsThreading::MatrixGeneration()
{
	srand(time(NULL));
	for (int i = 0; i < rows_matrix_A * colums_matrix_A; i++)
		matrix_A[i] = rand() % 9;

	cout<<"Matrix A generated\n";

	for (int i = 0; i < rows_matrix_B * colums_matrix_B; i++)
		matrix_B[i] = rand() % 9;

	cout<<"Matrix B generated\n";
	return 0;
}

int WindowsThreading::MatrixTranspose()
{
	for (int i=0; i<colums_matrix_B; i++)
		for (int j=0; j<rows_matrix_B; j++)
			matrix_B_transpose[j*colums_matrix_B+i] = matrix_B[j+i*rows_matrix_B];
	return 0;
}

void WindowsThreading::ThreadFunction(void*thread_number)
{
	int i,j,k,temp;
	for(i=(rows_matrix_A*(int)thread_number)/number_of_threads;i<(rows_matrix_A*((int)thread_number+1))/number_of_threads;i++)
	{
		for(j=0; j<colums_matrix_B; j++)
		{
			temp=0;
			for (k=0; k<rows_matrix_B; k++)
				temp = temp + (matrix_A[i*colums_matrix_B+k] * matrix_B_transpose[k+j*rows_matrix_B]);
			matrix_C[i*colums_matrix_B+j]=temp;
		}
	}		
}

void WindowsThreading::DeleteMemory()
{
	delete matrix_A;
	delete matrix_B;
	delete matrix_C;
	delete matrix_B_transpose;

	cout<<"Memory Deleted\n";
}

int WindowsThreading::MatrixStore()
{
	ofstream matrix_A_write("matrix_A.txt");
	ofstream matrix_B_write("matrix_B.txt");
	ofstream matrix_C_write("matrix_C.txt");
	for (int i = 0; i < rows_matrix_A; i++)
	{
		for (int j = 0; j < colums_matrix_A; j++)
			matrix_A_write << matrix_A[j+i*colums_matrix_A]<< "   ";
		matrix_A_write << endl;
	}
	for (int i = 0; i < rows_matrix_B; i++)
	{
		for (int j = 0; j < colums_matrix_B; j++)
			matrix_B_write << matrix_B[i*colums_matrix_B+j]<< "   ";
		matrix_B_write << endl;
	}
	for (int i = 0; i < rows_matrix_A; i++)
	{
		for (int j = 0; j < colums_matrix_B; j++)
			matrix_C_write << matrix_C[i*colums_matrix_A+j]<< "   ";
		matrix_C_write << endl;
	}
	matrix_A_write.close();
	matrix_B_write.close();
	matrix_C_write.close();
	return 0;
}
/*************************** MAIN**************************/
int main()
{ 
	if(colums_matrix_A!=rows_matrix_B)
	{
		cout<<"multiplication not possible"<<endl;
		return 0;
	}
	time_t start, end, diff;
	cout<<"Start\n";
	/***creating dynamic thread array***/
//	WindowsThreading Obj ;
	WindowsThreading::AssignMemory();
	WindowsThreading::MatrixGeneration();
	WindowsThreading Initializing;
//	WindowsThreading::MatrixStore();
	HANDLE* thread_array;
	thread_array = new HANDLE[number_of_threads];
	/***starting threads***/
	start = clock();
	WindowsThreading::MatrixTranspose();
	for(int i=0; i<number_of_threads; i++)
		thread_array[i] = (HANDLE)_beginthread(WindowsThreading::ThreadFunction,0,(void*)i);
	for (int i=0; i<number_of_threads; i++)
		WaitForSingleObject(thread_array[i], INFINITE);
	end = clock();
	diff = end - start;
	cout << "Time taken =  "<< diff << endl;
	cout<<endl<<"Multiplication done"<<endl;
	WindowsThreading::MatrixStore();
	WindowsThreading::DeleteMemory();
	delete thread_array;
	return 0;
}